package com.gitee.srurl.oid.gen;

import com.gitee.srurl.oid.domain.OidConstant;
import com.gitee.srurl.oid.domain.OidData;
import com.gitee.srurl.oid.domain.OidStoreData;
import com.gitee.srurl.oid.store.IOidStore;
import com.gitee.srurl.register.IDataReport;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author zxw
 * @date 2023/5/7 17:45
 */
@Slf4j
public class Oid implements IDataReport {

    /**
     * 当前序列号
     */
    private final AtomicLong sequence;

    /**
     * 批量最大ID
     */
    private volatile long batchMaxSequence;

    /**
     * 机器ID
     */
    private final long wokerId;

    /**
     * 是否开启批量获取ID
     */
    private final boolean useBatch;

    /**
     * 批量获取数量
     */
    private final int batchNum;

    /**
     * oid 存储
     */
    private final IOidStore oidStore;

    private final Lock lock = new ReentrantLock();

    public Oid(OidData oidData) {

        oidData = oidData == null ? OidData.builder().build() : oidData;
        IOidStore oidStore = oidData.getOidStore();
        if (oidStore == null) {
            log.error("oidStore 为空,初始化失败！");
            throw new NullPointerException("oidStore 不能为空！");
        }
        OidStoreData oidStoreData = oidStore.readCurrentData(oidData.getWorkerId());
        long currentSequence;
        if (oidStoreData == null) {
            currentSequence = 0L;
        } else {
            currentSequence = oidStoreData.getStoreSequence();
        }

        Long workerId = oidData.getWorkerId();
        if (workerId == null || workerId > OidConstant.MAX_WORKER_ID) {
            log.error("工作ID:{},最大ID:{},参数错误", workerId, OidConstant.MAX_WORKER_ID);
            throw new IllegalArgumentException("工作ID参数错误");
        }

        Boolean useBatch = oidData.getUseBatch();
        useBatch = useBatch != null;
        Integer batchNum = oidData.getBatchNum();
        if (useBatch) {
            if (batchNum == null || batchNum < 2) {
                log.warn("批量获取ID，批量数量为空或者小于2，设置为非批量获取，batchNum:{}", batchNum);
                useBatch = false;
                batchNum = 0;
            }
        } else {
            batchNum = 0;
        }

        this.wokerId = workerId;
        this.sequence = new AtomicLong(currentSequence);
        this.useBatch = useBatch;
        this.batchNum = batchNum;
        this.oidStore = oidStore;
        // 这个不用上报
//        DataReportHandle.registerReport(this);
    }

    public long nextId() {

        long nextId;
        if (!useBatch) {
            lock.lock();
            try {
                OidStoreData nextData = oidStore.getNextData(this.wokerId);
                // 不批量获取的话 不设置当前序列号数据
                nextId = nextData.getStoreSequence();
            } finally {
                lock.unlock();
            }
        } else {
            // 如果不进入if里面批量数据还够用 这边没有缓存 缓存的话 core包那边去实现 进入的话 获取新的一批数据
            if (this.batchMaxSequence <= this.sequence.get()) {
                lock.lock();
                if (this.batchMaxSequence <= this.sequence.get()) {
                    try {
                        // 获取新的一批数据
                        OidStoreData nextData = oidStore.getNextData(this.wokerId, true, batchNum);
                        Long nextMaxId = nextData.getStoreSequence();
                        if (nextMaxId <= this.sequence.get()) {
                            log.error("最新获取最大ID小于等于当前序列号，获取失败,当前序列号:{},返回参数:{}", sequence, nextData);
                            throw new IllegalArgumentException("当前ID获取异常！");
                        }
                        this.batchMaxSequence = nextMaxId;
                    } finally {
                        lock.unlock();
                    }
                }
            }

            nextId = this.sequence.addAndGet(1);
        }

        return 1L << OidConstant.FIX_ONE_INDEX | this.wokerId << OidConstant.SEQUENCE_BITS | nextId;
    }

    /**
     * 这个不用上报
     */
    @Override
    public String report() {

        return null;
    }

    /**
     * 这个不用上报
     */
    @Override
    public String reportPath() {
        return null;
    }
}
